

// printdev.c 
//
// created by Mike Podanoffsky
//            "Dissecting DOS"
// 
// make instructions:
// 
//    cl /AL printdev.c
// 
// 
// 

#include <string.h>
#include <stdio.h>
#include <dos.h>


// where DOS keeps the NULL driver.

#define _RXDOS_NULLDEVICE           0x0048


// DOS Device Header

#pragma pack(1)
typedef struct _DEVHEADER {

   void *      _NextDriver;
   int         _DevAttributes;
   int         _DevStrategy;
   int         _DevInterrupt;
   char        _DevName[ 8 ];

   } DEVHEADER, * LPDEVHEADER;
#pragma pack()

// DOS Device Header Flags

#define DEV_STDINPUT            0x0001
#define DEV_STDOUTPUT           0x0002
#define DEV_NULL                0x0004
#define DEV_CLOCK               0x0008

#define DEV_FASTCHARIO          0x0010               /* supports int 29 fast i/o    */
#define DEV_LOGICALMAPPING      0x0040               /* supports logical dev mapping    */
#define DEV_IOCTL               0x0080               /* supports IOCTL    */

#define DEV_REMOVABLEMEDIA      0x0800               /* supports removable media calls    */

#define DEV_OUTPUTTILLBUSY      0x2000               /* supports output till busy    */
#define DEV_IOCTL_RW            0x4000               /* supports IOCTL read/write    */
#define DEV_CHAR                0x8000               /* character mode device    */

#define DEV_BLOCKEXTNADDRESS    0x0002               /* block device support 32 bit extended address    */
#define DEV_REQUIRESFATSECTOR   0x2000               /* requires FAT sector address    */

///////////////////////////////////////////////////////////////

typedef struct _messages {

   int         Flags;
   char      * Message;

   }  MESSAGES;

MESSAGES Messages[] = {

   { DEV_STDINPUT,           "stdinput"                   },
   { DEV_STDOUTPUT,          "stdoutput"                  },
   { DEV_NULL,               "null"                       },
   { DEV_CLOCK,              "clock"                      },

   { DEV_FASTCHARIO,         "fastchario"                 },
   { DEV_LOGICALMAPPING,     "logicalmapping"             },
   { DEV_IOCTL,              "ioctl supported"            },

   { DEV_REMOVABLEMEDIA,     "removable media"            },

   { DEV_OUTPUTTILLBUSY,     "output till busy"           },
   { DEV_IOCTL_RW,           "ioctl_rw"                   },
   { DEV_CHAR,               "char"                       },

   { DEV_BLOCKEXTNADDRESS,   "blockextnaddress"           },
   { DEV_REQUIRESFATSECTOR,  "requiresfatsector"          },

   };

///////////////////////////////////////////////////////////////
static void printFlags( LPDEVHEADER lpDevHeader )
{

   int k;
   
   for ( k = 0; k < sizeof( Messages )/sizeof( MESSAGES ); ++k )
      {
      if ( lpDevHeader-> _DevAttributes & Messages[ k ]. Flags )
         printf( "    %s\n", Messages[ k ]. Message );

      }

   printf( "\n" );
}

///////////////////////////////////////////////////////////////
main()
{

   union _REGS r;
   struct _SREGS s;

   LPDEVHEADER  lpDevHeader = NULL;
   char         DeviceName[ 9 ];

   // first, we get the start of DOS memory (List of Lists)

   r.h.ah = 0x52;                      // use undoc function 52
   _intdosx( &r, &r, &s );             // es contains seg address of DOS

   FP_SEG( lpDevHeader ) = s.es;
   FP_OFF( lpDevHeader ) = _RXDOS_NULLDEVICE;

   printf ( "\n\n" );
   printf ( "  Address   Strategy  Interrupt  Name \n" );
   printf ( " ---------  --------  ---------  -------------------\n" );


   // then we loop through the device driver chain

   while ( FP_OFF( lpDevHeader ) != -1 )
      {

      // print current address, but not name.

      printf( " %04X:%04X    %04X      %04X     ", 
                  FP_SEG( lpDevHeader ),
                  FP_OFF( lpDevHeader ),
                  lpDevHeader-> _DevStrategy,
                  lpDevHeader-> _DevInterrupt );

      strncpy( DeviceName, lpDevHeader-> _DevName, 8 );
      DeviceName[ 8 ]= '\0';

      if ( lpDevHeader-> _DevAttributes & DEV_CHAR )
         printf( "%8s\n", DeviceName );

      else
         printf( "Logical Units: %d \n", lpDevHeader-> _DevName[ 0 ]   );

      printFlags( lpDevHeader );
      lpDevHeader = (LPDEVHEADER )lpDevHeader-> _NextDriver;
      }


}
